package main

import "fmt"

//Api数据指标 ApiInfo <public数据结构>
// Api数据指标
type ApiInfo struct {
	Api        string
	RequestCnt int64
	ErrCnt     int64
	DurOfSecs  int64
	TimeoutCnt int64 //add
}

//告警处理器群 Alertx
// 告警处理器接口
type AlertHandler interface {
	Check(info *ApiInfo)
}

// 告警处理器责任链
type Alertx struct {
	alertHandlersList []AlertHandler // 注册存储不同告警功能模块
}

func NewAlertx() *Alertx {
	return &Alertx{
		alertHandlersList: make([]AlertHandler, 0),
	}
}

//添加告警处理器
func (a *Alertx) AddHandler(aHandler AlertHandler) {
	a.alertHandlersList = append(a.alertHandlersList, aHandler)
}

//遍历责任链
func (a *Alertx) CheckAll(aInfo *ApiInfo) {
	for _, handler := range a.alertHandlersList {
		handler.Check(aInfo)
	}
}

// 报警规则
const (
	DEFAULT_RULE_MAXTIPS      = 99999
	DEFAULT_RULE_ERRCOUNT     = 99999
	DEFAULT_RULE_TIMEOUTCOUNT = 99999
)

type RuleData struct {
	maxTps          int64
	maxErrorCount   int64
	maxTimeOutCount int64 //add
}

type AlertRule struct {
	mpAlertConfig map[string]*RuleData
}

func NewAlertRule() *AlertRule {
	mp := make(map[string]*RuleData)
	//add:maxTimeOutCount的配置
	mp["getOrderInfo"] = &RuleData{maxTps: 100, maxErrorCount: 101, maxTimeOutCount: 5}
	mp["getEvaluates"] = &RuleData{maxTps: 200, maxErrorCount: 3, maxTimeOutCount: 5}
	return &AlertRule{mpAlertConfig: mp}
}

// tps阈值
func (a *AlertRule) GetTpsCount(api string) int64 {
	config, ok := a.mpAlertConfig[api]
	if ok {
		return config.maxTps
	}
	return DEFAULT_RULE_MAXTIPS
}

// 错误数阈值
func (a *AlertRule) GetErrCount(api string) int64 {
	config, ok := a.mpAlertConfig[api]
	if ok {
		return config.maxErrorCount
	}
	return DEFAULT_RULE_ERRCOUNT
}

//add: 获取超时数阈值
func (a *AlertRule) GetTimeoutCount(api string) int64 {
	config, ok := a.mpAlertConfig[api]
	if ok {
		return config.maxTimeOutCount
	}
	return DEFAULT_RULE_TIMEOUTCOUNT
}

// 告警通知

type EmergencyLevelType int

const (
	EL_URGENCY EmergencyLevelType = 0
	EL_SEVERE                     = 4
	EL_NORMAL                     = 5
)

type Notification struct{}

func (n *Notification) Notify(level EmergencyLevelType, msg string) {
	fmt.Printf("[Notify]Level:%v, msg:%s\n", level, msg)
}

//实现AlertHandler接口，按照TPS规则
type TpsAlertHandler struct {
	rule         *AlertRule
	notification *Notification
}

func NewTpsAlertHandler(rule *AlertRule, notification *Notification) *TpsAlertHandler {
	return &TpsAlertHandler{
		rule:         rule,
		notification: notification,
	}

}
func (t *TpsAlertHandler) Check(info *ApiInfo) {
	tps := info.RequestCnt / info.DurOfSecs
	if tps > t.rule.GetTpsCount(info.Api) {
		t.notification.Notify(EL_URGENCY, "TPS Too high!")
	}
	fmt.Println("TPS Alert Done")
}

//实现AlertHandler接口，按照Alert规则
type ErrAlertHandler struct {
	rule         *AlertRule
	notification *Notification
}

func NewErrAlertHandler(rule *AlertRule, notification *Notification) *ErrAlertHandler {
	return &ErrAlertHandler{
		rule:         rule,
		notification: notification,
	}
}

// 实现AlertHandler接口
func (e *ErrAlertHandler) Check(info *ApiInfo) {
	if info.ErrCnt > e.rule.GetErrCount(info.Api) {
		e.notification.Notify(EL_URGENCY, "ERR Count Too High!")
	}
	fmt.Println("Err Alert Done")
}

//add :新增超时监控维度，实现AlertHandler接口，按照Timeout规则
type TimeoutAlertHandler struct {
	rule         *AlertRule
	notification *Notification
}

func NewTimeoutAlertHandler(rule *AlertRule, notification *Notification) *TimeoutAlertHandler {
	return &TimeoutAlertHandler{
		rule:         rule,
		notification: notification,
	}
}

func (t *TimeoutAlertHandler) Check(info *ApiInfo) {
	if info.TimeoutCnt > t.rule.GetTimeoutCount(info.Api) {
		t.notification.Notify(EL_SEVERE, "Timeout Count Too High!")
	}
	fmt.Println("Timeout Alert Done")
}

func main() {
	alerts := NewAlertx()
	infos := &ApiInfo{
		Api:        "getOrderInfo",
		RequestCnt: 100,
		ErrCnt:     100,
		DurOfSecs:  1,
		TimeoutCnt: 100, //add :设置当前api的超时统计数
	}
	alerts.AddHandler(NewTpsAlertHandler(NewAlertRule(), &Notification{}))
	alerts.AddHandler(NewErrAlertHandler(NewAlertRule(), &Notification{}))
	alerts.AddHandler(NewTimeoutAlertHandler(NewAlertRule(), &Notification{})) //add:添加超时告警搞告警责任链
	alerts.CheckAll(infos)
}
